package com.beardedcoder.utils.ui;

import java.util.Collections;
import java.util.List;

import com.beardedcoder.utils.ui.view.MovableListView.DragListener;
import com.beardedcoder.utils.ui.view.MovableListView.DropListener;

import android.widget.BaseAdapter;

public abstract class MovableAdapter extends BaseAdapter implements DragListener, DropListener {
	private List<Object> data;
	private static final int INVISIBLE_INDEX_NONE = -99;
	protected int viewInMovement = INVISIBLE_INDEX_NONE;
	private ListenerDataAltered mListenerDataAltered = null;
	
	public MovableAdapter(List<Object> dataItems) {
		data = dataItems;
	}
	
	public void setListenerDataAltered(ListenerDataAltered listener) {
		mListenerDataAltered = listener;
	}
	
	/**
	 * Helper method to get the order of the data items contained. 
	 * A listener can be registered which will call back when
	 * the internal state of the data items have moved. 
	 * @return
	 */
	public List<Object> getDataItems() {
		return Collections.unmodifiableList(data);
	}

	@Override
	public int getCount() {
		return data.size();
	}

	@Override
	public Object getItem(int index) {
		return data.get(index);
	}

	@Override
	public long getItemId(int index) {
		return index;
	}

	public interface MovableView {
		void setData(Object data);
		Object getData();
	}
	
	@Override
	public void startDragging(int from) {
		viewInMovement = from;
		notifyDataSetChanged();
	}

	@Override
	public void drag(int from, int to) {
		moveItemHonouringIndexBounds(from, to);
		viewInMovement = to;
		notifyDataSetChanged();
	}

	/**
	 * because when moving items in and out we are actually removing them 
	 * from the list, it's possible our bounds gets messed up, therefore
	 * after removing check that 'to' is still valid. 
	 * @param fromIndex
	 * @param toIndex
	 */
	private void moveItemHonouringIndexBounds(int fromIndex, int toIndex) {
		Object removed = data.remove(fromIndex);
		if (toIndex >= data.size()) {
			data.add(removed);
		} else {
			data.add(toIndex, removed);			
		}
	}

	/**
	 * because this implementation actually reorders the list on 
	 * dragging, then this call through only needs to remove the 
	 * invisible index and call notifyDataSetChanged()
	 * If someone has registered to be made aware of any changes to 
	 * the internal state of the data items, they will get the 
	 * call through from here. 
	 */
	@Override
	public void drop(int from, int to) {
		viewInMovement = INVISIBLE_INDEX_NONE;
		notifyDataSetChanged();
		if (mListenerDataAltered != null) {
			mListenerDataAltered.newOrdering(data);
		}
	}
	
	public interface ListenerDataAltered {
		void newOrdering(List<Object> data);
	}
}
